﻿using System;
using System.ComponentModel;
using PostSharp.Aspects;
using PostSharp.Aspects.Advices;
using PostSharp.Extensibility;
using PostSharp.Reflection;

namespace BusinessFramework.AOP
{
    [Serializable]
    [IntroduceInterface(typeof(INotifyPropertyChanged), OverrideAction = InterfaceOverrideAction.Fail)]
    [MulticastAttributeUsage(MulticastTargets.Class, Inheritance = MulticastInheritance.Strict)]
    public sealed class ImplementNotifyPropertyChangedAttribute : InstanceLevelAspect, INotifyPropertyChanged
    {
        [IntroduceMember(Visibility = Visibility.Family, IsVirtual = true, 
            OverrideAction = MemberOverrideAction.Fail)]
        public void OnPropertyChanged(PropertyChangedEventArgs e)
        {
            if (PropertyChanged != null)
                PropertyChanged(Instance, e);
        }

        [IntroduceMember(OverrideAction = MemberOverrideAction.Fail)]
        public event PropertyChangedEventHandler PropertyChanged;

        [ImportMember("OnPropertyChanged", IsRequired = true, Order = ImportMemberOrder.AfterIntroductions)]
        public Action<PropertyChangedEventArgs> OnPropertyChangedMethod;

        [OnLocationSetValueAdvice,
         MulticastPointcut(Targets = MulticastTargets.Property, Attributes = MulticastAttributes.Instance | MulticastAttributes.NonAbstract)]
        public void OnPropertySet(LocationInterceptionArgs args)
        {
            // Don't go further if the new value is equal to the old one.
            if (args.Value == null && args.GetCurrentValue() == null) return;
            if (args.Value != null && args.Value.Equals( args.GetCurrentValue() )) return;

            // Actually sets the value.
            args.ProceedSetValue();

            OnPropertyChangedMethod(new PropertyChangedEventArgs(args.Location.Name));
        }
    }
}